package com.vhall.component.service.lottery.impl;


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.csv.CsvData;
import cn.hutool.core.text.csv.CsvReader;
import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.util.CharsetUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.vhall.component.dao.lottery.LotteryUserMapper;
import com.vhall.component.dao.room.RoomJoinsMapper;
import com.vhall.component.entity.account.vo.AccountVO;
import com.vhall.component.entity.interactive.vo.QuestionPageRspVO;
import com.vhall.component.entity.lottery.entity.LotteryUserEntity;
import com.vhall.component.entity.lottery.vo.*;
import com.vhall.component.entity.room.dto.RoomExtendsDTO;
import com.vhall.component.entity.room.dto.RoomJoinsEntityDTO;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.room.entity.RoomJoinsEntity;
import com.vhall.component.entity.room.vo.QueryRoomJoinsReqVO;
import com.vhall.component.entity.sign.vo.SignRecordPageVO;
import com.vhall.component.framework.common.constant.CommonConstant;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.framework.common.exception.GlobalErrorCode;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.constant.RoomRoleNameCons;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.VssToken;
import com.vhall.component.plugin.common.support.VssTokenThreadLocal;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.service.account.AccountService;
import com.vhall.component.service.interactive.QuestionService;
import com.vhall.component.service.lottery.LotteryRecordRepository;
import com.vhall.component.service.lottery.LotteryUserRepository;
import com.vhall.component.service.lottery.LotteryUserService;
import com.vhall.component.service.lottery.common.Consts;
import com.vhall.component.service.room.RoomInnerService;
import com.vhall.component.service.sign.service.SignInnerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 导入的抽奖用户
 *
 * @author longzhen.gong
 * @date 2021/5/31 17:05
 */

@Slf4j
@Service
@RefreshScope
public class LotteryUserServiceImpl implements LotteryUserService {

    private static final List<String> EXT_NAME_LIST = Arrays.asList("xlsx", "csv");

    /**
     * key序列化
     */
    private static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer();

    /**
     * value 序列化
     */
    private static final JdkSerializationRedisSerializer OBJECT_SERIALIZER = new JdkSerializationRedisSerializer();


    @Autowired
    private LotteryUserMapper lotteryUserMapper;


    @Autowired
    private PaasService paasService;

    @Autowired
    private LotteryUserRepository lotteryUserRepository;
    @Autowired
    private LotteryRecordRepository lotteryRecordRepository;


    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private Environment environment;


    @Value("${lottery.maxGetLotteryPersons:5000}")
    private Long canGenLotteryCount;


    @Autowired
    private RoomInnerService roomInnerService;

    @Autowired
    private QuestionService questionService;

    @Autowired
    private SignInnerService signInnerService;

    @Autowired
    private AccountService accountService;
    @Autowired
    private RoomJoinsMapper roomJoinsMapper;

    private final Pattern mobileP = Pattern.compile("^[1][3,4,5,6,7,8,9][0-9]{9}$");
    private final Pattern emailP = Pattern.compile("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$");

    @Override
    public String count(LotteryCountReqVO req) {
        long count;
        // 自定义列表
        if (req.getLotteryRule().equals(Consts.LOTTERY_RULE_CUSTOM)) {
            count = getLotteryUserData(req.getRoomId(), req.getWinnerOut());
        } else {// 指定列表
            count = getOnlineUserDataa(req);
        }
        String key = RedisKey.KEY_LOTTERY_RULE_TEXT + req.getRoomId();
        redisTemplate.opsForValue().set(key, req.getLotteryRuleText(), 86400, TimeUnit.SECONDS);

        if (count > canGenLotteryCount) {
            return canGenLotteryCount + "+";
        }
        return count + "";
    }

    @Override
    public Long total(LotteryCountReqVO req) {
        String roomId = req.getRoomId();
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(roomId);
//        获取当前在线用户
        Long userOnlineCount = paasService.getUserOnlineCount(roomInfo.getChannelId());
        if (userOnlineCount > 0) {
            return userOnlineCount;
        }

        return 0L;
    }

    /**
     * 从导入的用户进行 筛选
     *
     * @param roomId    room id
     * @param winnerOut 不允许重复中奖
     * @return count
     */
    private Integer getLotteryUserData(String roomId, Integer winnerOut) {
        RoomsEntityDTO room = roomInnerService.getRoomInfo(roomId);
        if (null == room) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        QueryWrapper<LotteryUserEntity> where = new QueryWrapper<>();

        where.eq("room_id", roomId);
        if (winnerOut.equals(Consts.LOTTERY_NOEXCLUDE_WINNINGED)) {
            where.eq("is_winner", 0);
        }
        List<LotteryUserEntity> users = lotteryUserMapper.selectList(where);
        if (CollectionUtils.isEmpty(users)) {
            return 0;
        }
        List<Integer> userIds = users.stream().map(LotteryUserEntity::getId).collect(Collectors.toList());

        String key = RedisKey.LOTTERY_RANGE_IMPORT + roomId;
        redisTemplate.delete(key);
        redisTemplate.executePipelined((RedisCallback<?>) connection -> {
            for (Integer id : userIds) {
                connection.sAdd(STRING_SERIALIZER.serialize(key), STRING_SERIALIZER.serialize(String.valueOf(id)));
            }
            return null;
        });
        return userIds.size();
    }

    private Long getOnlineUserDataa(LotteryCountReqVO req) {

        RoomsEntityDTO room = roomInnerService.getRoomInfo(req.getRoomId());
        if (null == room) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }

        RoomExtendsDTO roomExtendsDTO = roomInnerService.getRoomExtendsByRoomId(req.getRoomId());

        if (roomExtendsDTO == null) {
            throw new BusinessException("查询不到开播时间");
        }
        LocalDateTime startTime = roomExtendsDTO.getStartTime();

        String startTimeStr = DateUtil.format(startTime, CommonConstant.DATETIME_FORMAT);

        //pass查询人数
        int onlinePageSize = 1000;
        //业务查询数量
        int pageSize = 200;

        Long onlineCount = paasService.getUserOnlineCount(room.getChannelId());
        int totalPage = (int) Math.ceil(onlineCount * 1.0 / onlinePageSize);
        Set<Integer> resultIds = new HashSet<>();
        List<CompletableFuture<Set<Integer>>> futures = new ArrayList<>();

        QueryRoomJoinsReqVO queryRoomJoinsReqVO = new QueryRoomJoinsReqVO();
        //排除已中奖用户
        if (req.getWinnerOut() == 1) {
            queryRoomJoinsReqVO.setIsLotteryWinner(0);
        }
        queryRoomJoinsReqVO.setRoomId(req.getRoomId());
        switch (req.getLotteryType()) {
            case 2:
            case 6:
                //2参与问卷的参会者, 6参与问卷的观众
                //参与问卷的参会者  先查问卷 再筛选在线 再筛选排除已经中奖
                queryRoomJoinsReqVO.setIsAnsweredQuestionnaire(1);
                //参与问卷的参会者
                if (req.getLotteryType() == 2) {
                    queryRoomJoinsReqVO.setRoleName(RoomRoleNameCons.ROLE_OWNER);
                    queryRoomJoinsReqVO.setRoleNameOperate(SqlKeyword.GT.getSqlSegment());
                } else if (req.getLotteryType() == 6) {
                    //参与问卷的观众
                    queryRoomJoinsReqVO.setRoleName(RoomRoleNameCons.ROLE_AUDIENCE);
                    queryRoomJoinsReqVO.setRoleNameOperate(SqlKeyword.EQ.getSqlSegment());
                }
                QuestionPageRspVO<Integer> queryRsp = questionService.queryQuesAccounts(room.getRoomId(), startTimeStr, 1, pageSize, true);
                Integer totalAccounts = queryRsp.getTotalCount();
                List<Integer> firstQuestData = queryRsp.getData();
                Set<Integer> firstPaga = winoutAtOnline(req, queryRoomJoinsReqVO, room, firstQuestData);
                resultIds.addAll(firstPaga);
                int totalQpage = (int) Math.ceil(totalAccounts * 1.0 / pageSize);
                for (int i = 2; i <= totalQpage; i++) {
                    final int j = i;
                    CompletableFuture<Set<Integer>> future = CompletableFuture.supplyAsync(() -> {
                        QueryRoomJoinsReqVO queryRoomJoinReqVO = new QueryRoomJoinsReqVO();
                        QueryWrapper<RoomJoinsEntity> wrapper = new QueryWrapper<>();
                        if (req.getLotteryType() == 2) {//参与问卷的参会者
                            queryRoomJoinReqVO.setRoleName(RoomRoleNameCons.ROLE_OWNER);
                            queryRoomJoinReqVO.setRoleNameOperate(SqlKeyword.GT.getSqlSegment());
                        } else if (req.getLotteryType() == 6) { //参与问卷的观众
                            queryRoomJoinReqVO.setRoleName(RoomRoleNameCons.ROLE_AUDIENCE);
                            queryRoomJoinReqVO.setRoleNameOperate(SqlKeyword.EQ.getSqlSegment());
                        }
                        //排除已中奖用户
                        if (req.getWinnerOut() == 1) {
                            queryRoomJoinReqVO.setIsLotteryWinner(0);
                        }
                        wrapper.select("account_id");
                        queryRoomJoinReqVO.setRoomId(req.getRoomId());
                        wrapper.eq("room_id", req.getRoomId());
                        return question(req, startTimeStr, queryRoomJoinReqVO, room, j, pageSize);
                    });
                    futures.add(future);
                    if (i % 5 == 0 || i == totalQpage) {
                        Iterator<CompletableFuture<Set<Integer>>> iterator = futures.iterator();
                        while (iterator.hasNext()) {
                            CompletableFuture<Set<Integer>> result = iterator.next();
                            try {
                                resultIds.addAll(result.get());
                            } catch (InterruptedException e) {
                                log.error("获取数据失败", e);
                                Thread.currentThread().interrupt();
                                throw new BusinessException(GlobalErrorCode.FAILED);
                            } catch (ExecutionException e) {
                                log.error("获取数据失败", e);
                                throw new BusinessException(GlobalErrorCode.FAILED);
                            }

                            iterator.remove();
                        }
                    }
                }
                break;
            //全体参会用户 先查在线人员。再查room_joins
            case 1:
                //4 全体观众用户 先查在线人员。再查room_joins观众
            case 4:
                //5 已经登陆的观众先查在线人员，再按账号筛选。
            case 5:
                async(totalPage, 1, onlinePageSize, req, room, resultIds);
                break;
            case 3:
            case 7:
                //3参与签到的参会者
                //7参与签到的观众
                //参与问卷的参会者  先查问卷 再筛选在线 再筛选排除已经中奖
                if (req.getLotteryType() == 3) {
                    //参与签到的参会者
                    queryRoomJoinsReqVO.setRoleName(RoomRoleNameCons.ROLE_OWNER);
                    queryRoomJoinsReqVO.setRoleNameOperate(SqlKeyword.GT.getSqlSegment());
                } else if (req.getLotteryType() == 7) {
                    //参与签到的观众
                    queryRoomJoinsReqVO.setRoleName(RoomRoleNameCons.ROLE_AUDIENCE);
                    queryRoomJoinsReqVO.setRoleNameOperate(SqlKeyword.EQ.getSqlSegment());
                }
                queryRoomJoinsReqVO.setIsSigned(1);
                SignRecordPageVO<Integer> signRecordPageVO = signInnerService.selectSignAfterLiveStart(room.getRoomId(), startTimeStr, 1, pageSize, true);
                int totalCount = signRecordPageVO.getTotalCount();
                List<Integer> firstSignData = signRecordPageVO.getData();
                Set<Integer> signTemp = winoutAtOnline(req, queryRoomJoinsReqVO, room, firstSignData);
                resultIds.addAll(signTemp);
                int totalSpage = (int) Math.ceil(totalCount * 1.0 / pageSize);
                for (int i = 2; i <= totalSpage; i++) {
                    final int j = i;
                    CompletableFuture<Set<Integer>> future = CompletableFuture.supplyAsync(() -> {
                                QueryWrapper<RoomJoinsEntity> signWraper = new QueryWrapper<>();
                                QueryRoomJoinsReqVO queryRoomJoinSign = new QueryRoomJoinsReqVO();
                                if (req.getWinnerOut() == 1) {
                                    queryRoomJoinSign.setIsLotteryWinner(0);
                                }
                                queryRoomJoinSign.setRoomId(req.getRoomId());
                                if (req.getLotteryType() == 3) { //参与签到的参会者
                                    queryRoomJoinSign.setRoleNameOperate(SqlKeyword.GT.getSqlSegment());
                                    queryRoomJoinSign.setRoleName(RoomRoleNameCons.ROLE_OWNER);
                                } else if (req.getLotteryType() == 7) {     //参与签到的观众
                                    queryRoomJoinSign.setRoleNameOperate(SqlKeyword.EQ.getSqlSegment());
                                    queryRoomJoinSign.setRoleName(RoomRoleNameCons.ROLE_AUDIENCE);
                                }
                                queryRoomJoinSign.setIsSigned(1);
                                signWraper.eq("is_signed", 1);
                                return sign(req, startTimeStr, queryRoomJoinSign, room, j, pageSize);
                            }
                    );
                    futures.add(future);
                    if (i % 5 == 0 || i == totalSpage) {
                        Iterator<CompletableFuture<Set<Integer>>> iterator = futures.iterator();
                        while (iterator.hasNext()) {
                            CompletableFuture<Set<Integer>> result = iterator.next();
                            try {
                                resultIds.addAll(result.get());
                            } catch (InterruptedException e) {
                                log.error("获取数据失败", e);
                                Thread.currentThread().interrupt();
                                throw new BusinessException(GlobalErrorCode.FAILED);
                            } catch (ExecutionException e) {
                                log.error("获取数据失败", e);
                                throw new BusinessException(GlobalErrorCode.FAILED);
                            }

                            iterator.remove();
                        }
                    }
                }
                break;
            default:
                break;
        }
        String key = RedisKey.LOTTERY_RANGE_ACCOUNTS + req.getRoomId();
        redisTemplate.delete(key);
        redisTemplate.executePipelined((RedisCallback<?>) connection -> {
            for (Integer id : resultIds) {
                connection.sAdd(Objects.requireNonNull(STRING_SERIALIZER.serialize(key)), STRING_SERIALIZER.serialize(String.valueOf(id)));
            }
            return null;
        });
        return redisTemplate.opsForSet().size(key);
    }

    private Set<Integer> sign(LotteryCountReqVO req, String startTimeStr, QueryRoomJoinsReqVO queryRoomJoinSign,
                              RoomsEntityDTO room, int page, int pageSize) {
        SignRecordPageVO<Integer> rspVO = signInnerService.selectSignAfterLiveStart(room.getRoomId(), startTimeStr, page, pageSize, false);
        List<Integer> firstSignData = rspVO.getData();
        return winoutAtOnline(req, queryRoomJoinSign, room, firstSignData);
    }


    /**
     * 全体观众和全体参会者使用
     *
     * @param
     * @param totalPage
     * @param startIndex
     * @param onlinePageSize
     * @param
     * @return
     * @throws Exception
     */
    private Set<Integer> async(
            int totalPage, int startIndex, int onlinePageSize,
            LotteryCountReqVO req, RoomsEntityDTO room, Set<Integer> accountIds) {
        List<CompletableFuture<Set<Integer>>> futures = new ArrayList<>();
        String channelId = room.getChannelId();
        for (int i = startIndex; i <= totalPage; i++) {
            final int j = i;
            QueryRoomJoinsReqVO queryRoomJoinsReqVO = new QueryRoomJoinsReqVO();
            if (req.getWinnerOut() == 1) {
                queryRoomJoinsReqVO.setIsLotteryWinner(0);
            }
            queryRoomJoinsReqVO.setRoomId(room.getRoomId());
            switch (req.getLotteryType()) {
                case 1:
                    //全体参会用户
                    queryRoomJoinsReqVO.setRoleName("1");
                    queryRoomJoinsReqVO.setRoleNameOperate(">");
                    break;
                //4 全体观众用户 先查在线人员。再查room_joins观众
                case 4:
                    //5 已经登陆的观众先查在线人员，再按账号筛选。
                case 5:
                    queryRoomJoinsReqVO.setRoleName("2");
                    queryRoomJoinsReqVO.setRoleNameOperate("=");
                    break;
                default:
                    break;
            }
            CompletableFuture<Set<Integer>> future = CompletableFuture.supplyAsync(() ->
                    watcher(req.getLotteryType() == 5, queryRoomJoinsReqVO, channelId, j, onlinePageSize)
            );
            futures.add(future);
            if (i % 5 == 0 || i == totalPage) {
                Iterator<CompletableFuture<Set<Integer>>> iterator = futures.iterator();
                while (iterator.hasNext()) {
                    CompletableFuture<Set<Integer>> result = iterator.next();
                    try {
                        accountIds.addAll(result.get());
                    } catch (InterruptedException e) {
                        log.error("获取数据失败", e);
                        Thread.currentThread().interrupt();
                        throw new BusinessException(GlobalErrorCode.FAILED);
                    } catch (ExecutionException e) {
                        log.error("获取数据失败", e);
                        throw new BusinessException(GlobalErrorCode.FAILED);
                    }
                    iterator.remove();
                }
               /* if (accountIds.size() > canGenLotteryCount) {
                    break;
                }*/
            }
        }
        return accountIds;
    }

    /**
     * 先查在线人，再过滤参会者
     *
     * @param loginedWatcher
     * @param queryRoomJoinsReqVO
     * @param channelId
     * @param onlinePage
     * @param onlinePageSize
     * @return
     */
    private Set<Integer> watcher(boolean loginedWatcher, QueryRoomJoinsReqVO queryRoomJoinsReqVO,
                                 String channelId, int onlinePage, int onlinePageSize) {
        List<Integer> ids = paasService.getUserIdList(channelId, onlinePage, onlinePageSize);
        //过滤已经登陆用户
        if (loginedWatcher) {
            String idStr = ids.stream().map(Object::toString).collect(Collectors.joining(","));
            List<AccountVO> entities = accountService.listByIds(idStr);
            ids = entities.stream().filter(accountVO -> accountVO.getAccountType() == 2).map(AccountVO::getAccountId).collect(Collectors.toList());
        }
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptySet();
        }
        queryRoomJoinsReqVO.setAccountIds(ids.stream().map(Object::toString).collect(Collectors.joining(",")));
        List<RoomJoinsEntityDTO> roomJoinsEntities = roomInnerService.getRoomJoinsList(queryRoomJoinsReqVO);
        // 排除APP登录用户 deviceType：0=未知设备,1=h5手机端,2=pc,3=sdk,4=app
        return new HashSet<>(roomJoinsEntities.stream().filter(j -> j.getDeviceType() < 4).map(RoomJoinsEntityDTO::getAccountId).collect(Collectors.toList()));
    }

    private Set<Integer> question(LotteryCountReqVO req, String startTimeStr, QueryRoomJoinsReqVO queryRoomJoinsReqVO,
                                  RoomsEntityDTO room, int page, int pageSize) {
        QuestionPageRspVO<Integer> queryRsp = questionService.queryQuesAccounts(room.getRoomId(), startTimeStr, page, pageSize, false);
        List<Integer> pageData = queryRsp.getData();
        return winoutAtOnline(req, queryRoomJoinsReqVO, room, pageData);
    }

    private Set<Integer> winoutAtOnline(LotteryCountReqVO req, QueryRoomJoinsReqVO queryRoomJoinsReqVO, RoomsEntityDTO room,
                                        List<Integer> pageData) {
        if (CollectionUtils.isEmpty(pageData)) {
            return Collections.emptySet();
        }

        StringJoiner joiner = new StringJoiner(",");
        for (Integer pageDatum : pageData) {
            joiner.add(pageDatum.toString());
        }
        queryRoomJoinsReqVO.setAccountIds(joiner.toString());

        List<RoomJoinsEntityDTO> roomJoinsEntities = roomInnerService.getRoomJoinsList(queryRoomJoinsReqVO);
        List<Integer> filterRoomAccounts = new ArrayList<>(roomJoinsEntities.stream().map(RoomJoinsEntityDTO::getAccountId).collect(Collectors.toList()));
        if (CollectionUtils.isEmpty(filterRoomAccounts)) {
            return Collections.emptySet();
        }
        //筛选在线的人
        Map<String, Object> onlineQuesResult = paasService.checkUserOnlineByChannel(room.getChannelId(), filterRoomAccounts);
        return new HashSet<>(onlineQuesResult.entrySet().stream().filter(entity -> (Integer) entity.getValue() == 1)
                .map(entity -> Integer.valueOf(entity.getKey())).collect(Collectors.toList()));
    }


    private List<LotterySearchRspVO> getLotteryJoinsByKeyword(LotterySearchReqVO req) {

        RoomsEntityDTO room = roomInnerService.getRoomInfo(req.getRoomId());
        if (null == room) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }

        QueryRoomJoinsReqVO queryRoomJoinsReqVO = new QueryRoomJoinsReqVO();

        queryRoomJoinsReqVO.setRoomId(req.getRoomId());
        queryRoomJoinsReqVO.setNickname(req.getKeyword());
        queryRoomJoinsReqVO.setUsername(req.getKeyword());
        queryRoomJoinsReqVO.setNickname(req.getKeyword());
        switch (req.getLotteryType()) {
            case 1:
                // 全体参会用户
                queryRoomJoinsReqVO.setRoleName(RoomRoleNameCons.ROLE_OWNER);
                queryRoomJoinsReqVO.setRoleNameOperate(SqlKeyword.GT.getSqlSegment());
                break;
            case 2:
                //参与问卷的参会者
                queryRoomJoinsReqVO.setRoleName(RoomRoleNameCons.ROLE_OWNER);
                queryRoomJoinsReqVO.setRoleNameOperate(SqlKeyword.GT.getSqlSegment());
                queryRoomJoinsReqVO.setIsAnsweredQuestionnaire(1);
                break;

            case 3:
                //参与签到的参会者
                queryRoomJoinsReqVO.setRoleName(RoomRoleNameCons.ROLE_OWNER);
                queryRoomJoinsReqVO.setRoleNameOperate(SqlKeyword.GT.getSqlSegment());
                queryRoomJoinsReqVO.setIsSigned(1);
                break;
            case 4:
                //全体观众用户
                queryRoomJoinsReqVO.setRoleName(RoomRoleNameCons.ROLE_AUDIENCE);
                queryRoomJoinsReqVO.setRoleNameOperate(SqlKeyword.EQ.getSqlSegment());
                break;
            case 5:
                //已登录的观众
                queryRoomJoinsReqVO.setRoleName(RoomRoleNameCons.ROLE_AUDIENCE);
                queryRoomJoinsReqVO.setRoleNameOperate(SqlKeyword.EQ.getSqlSegment());
                break;
            case 6:
                //参与问卷的观众
                queryRoomJoinsReqVO.setRoleName(RoomRoleNameCons.ROLE_AUDIENCE);
                queryRoomJoinsReqVO.setRoleNameOperate(SqlKeyword.EQ.getSqlSegment());
                queryRoomJoinsReqVO.setIsAnsweredQuestionnaire(1);
                break;
            case 7:
                //参与问卷的观众
                queryRoomJoinsReqVO.setRoleName(RoomRoleNameCons.ROLE_AUDIENCE);
                queryRoomJoinsReqVO.setRoleNameOperate(SqlKeyword.EQ.getSqlSegment());
                queryRoomJoinsReqVO.setIsSigned(1);
                break;
            default:
                break;
        }

        List<RoomJoinsEntityDTO> roomJoinsEntities = roomInnerService.getRoomJoinsList(queryRoomJoinsReqVO);
        //登陆
        if (req.getLotteryType() == 5) {
            String idStr = roomJoinsEntities.stream().map(x -> String.valueOf(x.getAccountId())).collect(Collectors.joining(","));
            if (StringUtils.isNotBlank(idStr)) {
                List<AccountVO> accountList = accountService.listByIds(idStr);
                Map<Integer, Integer> map = accountList.stream().filter(x -> x.getAccountType() == 2).
                        collect(Collectors.toMap(AccountVO::getAccountId, x -> 1, (k1, k2) -> k2));
                roomJoinsEntities = roomJoinsEntities.stream().filter(x -> map.get(x.getAccountId()) != null).collect(Collectors.toList());
            }

        }

        if (CollectionUtils.isNotEmpty(roomJoinsEntities)) {
            Map<String, Object> map = paasService.checkUserOnlineByChannel(room.getChannelId(),
                    roomJoinsEntities.stream().map(RoomJoinsEntityDTO::getAccountId).collect(Collectors.toList()));
            roomJoinsEntities = roomJoinsEntities.stream().filter(x -> (Integer) map.get(x.getAccountId() + "") == 1).collect(Collectors.toList());
        }

        List<LotterySearchRspVO> lsrspvos = new ArrayList<>();
        for (RoomJoinsEntityDTO roomJoinsEntity : roomJoinsEntities) {
            LotterySearchRspVO lotterySearchRspVO = new LotterySearchRspVO();
            BeanUtils.copyProperties(roomJoinsEntity, lotterySearchRspVO);
            lsrspvos.add(lotterySearchRspVO);
        }
        return lsrspvos;
    }


    private List<LotterySearchRspVO> getLotteryUserByKeyword(String roomId, String keyWord) {
        RoomsEntityDTO room = roomInnerService.getRoomInfo(roomId);
        if (null == room) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }

        List<LotterySearchRspVO> lotterySearchRsps = new ArrayList<>();
        LambdaQueryWrapper<LotteryUserEntity> wrapper = new LambdaQueryWrapper();
        wrapper.eq(LotteryUserEntity::getRoomId, roomId);
        wrapper.and(w -> w.eq(LotteryUserEntity::getNickname, keyWord).or().eq(LotteryUserEntity::getUsername, keyWord));
        wrapper.select(LotteryUserEntity::getId, LotteryUserEntity::getUsername, LotteryUserEntity::getNickname);
        List<LotteryUserEntity> lotteryUserEntities = lotteryUserMapper.selectList(wrapper);
        String keyLotteryUser = RedisKey.LOTTERY_RANGE_IMPORT + roomId;

        for (LotteryUserEntity lotteryUserEntity : lotteryUserEntities) {
            if (Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(keyLotteryUser, lotteryUserEntity.getId() + ""))) {
                LotterySearchRspVO lotterySearchRspVO = new LotterySearchRspVO();
                BeanUtils.copyProperties(lotteryUserEntity, lotterySearchRspVO);
                lotterySearchRsps.add(lotterySearchRspVO);
            }
        }
        return lotterySearchRsps;
    }


    @Override
    public List<LotterySearchRspVO> search(LotterySearchReqVO req) {
        List<LotterySearchRspVO> lotterySearchRsps;
        // 自定义列表
        if (req.getLotteryRule() == Consts.LOTTERY_RULE_CUSTOM) {
            lotterySearchRsps = getLotteryUserByKeyword(req.getRoomId(), req.getKeyword());
        } else {
            // 指定列表
            lotterySearchRsps = getLotteryJoinsByKeyword(req);
        }
        return lotterySearchRsps;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public LotteryImportRspVO importUser(LotteryImportReqVO req) {
        MultipartFile file = req.getFile();
        if (file == null || StringUtils.isBlank(file.getOriginalFilename())) {
            throw new BusinessException(BizErrorCode.COMP_LOTTERY_IMPORT_NO_FILE_ERROR);
        }
        VssToken vssToken = VssTokenThreadLocal.getToken();
        RoomsEntityDTO room = roomInnerService.getRoomInfo(req.getRoomId());
        if (null == room) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        String fileName = file.getOriginalFilename();
        String extName = FileUtil.extName(fileName);
        if (!EXT_NAME_LIST.contains(extName)) {
            throw new BusinessException(BizErrorCode.TYPE_FILE_INVALID);
        }
        if ("csv".equals(extName)) {
            return parseCsv(file, room, fileName, vssToken);
        } else {
            return parseXslx(file, room, fileName, vssToken);
        }
    }


    private LotteryImportRspVO parseCsv(MultipartFile multipartFile, RoomsEntityDTO room, String fileName, VssToken vssToken) {
        // 解析csv文件
        CsvReader csvReader = new CsvReader();
        try {
            File file = com.vhall.component.plugin.common.utils.FileUtil.multiToFile(multipartFile);
            String fileCharset = com.vhall.component.plugin.common.utils.FileUtil.getFileCharset(file);
            CsvData csvData = csvReader.read(file, CharsetUtil.charset(fileCharset));
            List<CsvRow> rows = csvData.getRows();
            for (int i = rows.size() - 1; i >= 0; i--) {
                if (StringUtils.isBlank(rows.get(i).get(0)) && StringUtils.isBlank(rows.get(i).get(1))) {
                    rows.remove(i);
                }
            }
            int totalNums = rows.size();
            if (totalNums > 4000) {
                throw new BusinessException(BizErrorCode.COMP_LOTTERY_UPLOAD_LIMIT_FAILURE);
            }
            if (totalNums < 2) {
                throw new BusinessException(BizErrorCode.COMP_LOTTERY_UPLOAD_NULL_FAILURE);
            }
            List<String> errData = new ArrayList<>();
            List<LotteryUserEntity> lotteryUserEntities = new ArrayList<>();
            for (int i = 1; i < totalNums; i++) {
                CsvRow csvRow = rows.get(i);
                String userName = csvRow.get(0);
                String nickname = csvRow.get(1);
                if (userName == null || nickname == null) {
                    errData.add(i + "行空值");
                    continue;
                }
                Boolean check = addErrData(errData, userName, nickname, lotteryUserEntities);
                if (!check) {
                    continue;
                }
                addLotteryUserEntities(room, fileName, userName, nickname, vssToken, lotteryUserEntities);
            }
            return getLotteryImportRspVO(room, lotteryUserEntities, errData, fileName);
        } catch (Exception e) {
            log.error("导入excel,parseCsv失败", e);
            throw new BusinessException(e.getMessage());
        }
    }

    private LotteryImportRspVO parseXslx(MultipartFile file, RoomsEntityDTO room, String fileName, VssToken vssToken) {
        // 解析xlsx文件
        try (Workbook wb = new XSSFWorkbook(file.getInputStream())) {
            Sheet sheet = wb.getSheetAt(0);
            int totalNums = sheet.getPhysicalNumberOfRows();
            if (totalNums > 3001) {
                throw new BusinessException(BizErrorCode.COMP_LOTTERY_UPLOAD_LIMIT_FAILURE);
            }
            if (totalNums < 2) {
                throw new BusinessException(BizErrorCode.COMP_LOTTERY_UPLOAD_NULL_FAILURE);
            }
            int lastRowNum = sheet.getLastRowNum();
            List<String> errData = new ArrayList<>();
            List<LotteryUserEntity> lotteryUserEntities = new ArrayList<>();
            for (int i = 1; i <= lastRowNum; i++) {
                Row row = sheet.getRow(i);
                if (row == null) {
                    continue;
                }
                Cell one = row.getCell(0);
                Cell two = row.getCell(1);
                if (two == null || one == null) {
                    errData.add(i + "行空值");
                    continue;
                }
                one.setCellType(CellType.STRING);
                two.setCellType(CellType.STRING);
                String userName = row.getCell(0).getStringCellValue();
                String nickname = row.getCell(1).getStringCellValue();
                Boolean check = addErrData(errData, userName, nickname, lotteryUserEntities);
                if (!check) {
                    continue;
                }
                addLotteryUserEntities(room, fileName, userName, nickname, vssToken, lotteryUserEntities);
            }
            return getLotteryImportRspVO(room, lotteryUserEntities, errData, fileName);
        } catch (IOException e) {
            log.error("导入excel,parseXslx失败", e);
            throw new BusinessException(e.getMessage());
        }
    }

    private void addLotteryUserEntities(RoomsEntityDTO room, String fileName, String userName, String nickname,
                                        VssToken vssToken, List<LotteryUserEntity> lotteryUserEntities) {
        LotteryUserEntity lotteryUserEntity = new LotteryUserEntity();
        lotteryUserEntity.setIlId(room.getIlId());
        lotteryUserEntity.setRoomId(room.getRoomId());
        lotteryUserEntity.setTitle(fileName);
        lotteryUserEntity.setUsername(userName);
        lotteryUserEntity.setNickname(nickname);
        lotteryUserEntity.setAppId(vssToken.getAppId());
        lotteryUserEntities.add(lotteryUserEntity);
    }

    private Boolean addErrData(List<String> errData, String userName, String nickname, List<LotteryUserEntity> lotteryUserEntities) {
        if (!mobileP.matcher(userName).matches() && !emailP.matcher(userName).matches()) {
            errData.add(userName + "格式不正确");
            return false;
        }
        if (StringUtils.length(nickname) > 100) {
            errData.add(nickname + "长度超限制");
            return false;
        }
        return lotteryUserEntities.stream().noneMatch(x -> x.getUsername().equalsIgnoreCase(userName));
    }


    private LotteryImportRspVO getLotteryImportRspVO(RoomsEntityDTO room, List<LotteryUserEntity> lotteryUserEntities,
                                                     List<String> errData, String fileName) {
        if (lotteryUserEntities.size() > 3000) {
            throw new BusinessException(BizErrorCode.COMP_LOTTERY_UPLOAD_LIMIT_FAILURE);
        }
        LambdaQueryWrapper<LotteryUserEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(LotteryUserEntity::getIlId, room.getIlId());
        lotteryUserMapper.delete(wrapper);
        lotteryUserRepository.saveBatch(lotteryUserEntities);
        LotteryImportRspVO lotteryImportRspVO = new LotteryImportRspVO();
        lotteryImportRspVO.setErrCount(errData.size());
        lotteryImportRspVO.setFileName(fileName);
        lotteryImportRspVO.setCount(lotteryUserEntities.size());
        return lotteryImportRspVO;
    }


}
